package cn.bugstack.mybatis.datasource.pooled;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Objects;
import java.util.logging.Logger;

/**
 * description: 池代理连接
 * taskId：
 * <pre>
 * change history:
 * date             defect#             person                comments
 * ---------------------------------------------------------------------
 * 2024/11/29       ********         zhaochaoyue        create file
 * </pre>
 *
 * @author: zhaochaoyue
 * @date: 2024/11/29 10:22
 * @copyright: 2024
 */
public class PooledConnection implements InvocationHandler {
    
    private static final String CLOSE ="close";
    private static final Class<?>[] IFACES = new Class<?>[]{Connection.class};
    
    private int hashCode =0;
    private PooledDataSource dataSource;
    
    //真实的连接
    private Connection realConnection;
    
    //代理的连接
    private Connection proxyConnection;
    
    private long checkoutTimestamp;
    private long createdTimestamp;
    //最后使用时间
    private long lastUsedTimestamp;
    private int connectionTypeCode;
    private boolean valid;

    public PooledConnection(Connection connection,PooledDataSource dataSource) {
        this.hashCode =connection.hashCode();
        this.realConnection =connection;
        this.dataSource = dataSource;
        this.createdTimestamp = System.currentTimeMillis();
        this.lastUsedTimestamp= System.currentTimeMillis();
        this.valid = true;
        this.proxyConnection = (Connection)Proxy.newProxyInstance(Connection.class.getClassLoader(),IFACES,this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        //如果调用Close 关闭连接方法则将连接加入连接池中 并返回null
        if(CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)){
            dataSource.pushConnection(this);
            return null;
        }else{
            if(!Object.class.equals(method.getDeclaringClass())){
                //除了toString()方法，其他方法调用之前要检查connection是否还是合法的,不合法要抛出SQLException
                checkConnection();
            }
            //其他方法交给connection去掉用
            return method.invoke(realConnection,args);
        }
    }

    private void checkConnection() throws SQLException {
        if(!valid){
            throw new SQLException("Error accessing PooledConnection.Connectin is invalid.");
        }
    }
    
    public void invalidate(){
        valid =false;
    }
    
    public boolean isValid(){
        return valid && realConnection !=  null && dataSource.pingConnection(this);
    }

    public int getHashCode() {
        return hashCode;
    }

    public PooledDataSource getDataSource() {
        return dataSource;
    }

    public Connection getRealConnection() {
        return realConnection;
    }

    public Connection getProxyConnection() {
        return proxyConnection;
    }
    
    public int getRealHashCode(){
        return realConnection == null ?0:realConnection.hashCode();
    }

    public long getCheckoutTimestamp() {
        return System.currentTimeMillis() - checkoutTimestamp;
    }

    public void setCreatedTimestamp(long createdTimestamp) {
        this.createdTimestamp = createdTimestamp;
    }

    public long getCreatedTimestamp() {
        return createdTimestamp;
    }

    public long getLastUsedTimestamp() {
        return lastUsedTimestamp;
    }

    public void setLastUsedTimestamp(long lastUsedTimestamp) {
        this.lastUsedTimestamp = lastUsedTimestamp;
    }

    public int getConnectionTypeCode() {
        return connectionTypeCode;
    }

    public void setConnectionTypeCode(int connectionTypeCode) {
        
        this.connectionTypeCode = connectionTypeCode;
    }

    public void setCheckoutTimestamp(long checkoutTimestamp) {
        this.checkoutTimestamp = checkoutTimestamp;
    }
    
    public  long getTimeElapsedSinceLastUse(){
        return System.currentTimeMillis() -lastUsedTimestamp;
    }
    public long getAge(){
        return System.currentTimeMillis() -createdTimestamp;
    }
    

    @Override
    public boolean equals(Object o) {
        if(o instanceof PooledConnection){
            return realConnection.hashCode() == (((PooledConnection)o).realConnection.hashCode());
        } else if (o instanceof Connection) {
            return hashCode == o.hashCode();
        }else {
            return false;
        }
            
    }

    @Override
    public int hashCode() {
        return hashCode;
    }
}
